package org.atlantis.grosbeak.json;

import java.io.IOException;
import java.io.Reader;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.atlantis.grosbeak.castor.Castors;
import org.atlantis.grosbeak.lang.Exceptions;
import org.atlantis.grosbeak.lang.Mirror;
import org.atlantis.grosbeak.lang.Strings;

/**
 * JSON解析器，对JSON内容进行解析。
 * 
 * @author <a href="mailto:seenoevil.cn@gmail.com">SeeNoEvil</a>
 * 
 */
class JsonAnalyzer {

	private int cursor;
	private Reader reader;
	private int col;
	private int row;

	JsonAnalyzer(Reader reader) {
		this.reader = reader;
		this.col = 0;
		this.row = 1;
	}

	private int nextChar() throws IOException {
		if (-1 == cursor)
			return -1;
		try {
			cursor = reader.read();
			if (cursor == '\n') {
				row++;
				col = 0;
			} else {
				col++;
			}
		} catch (Exception e) {
			cursor = -1;
		}
		return cursor;
	}

	// 忽略空白字符
	private void ignoreBlank() throws IOException {
		while (cursor >= 0 && cursor <= 32)
			nextChar();
	}

	// 忽略多行注释
	private void ignoreBlockComment() throws IOException {
		nextChar();
		while (cursor != -1) {
			if (cursor == '*') {
				if (nextChar() == '/')
					break;
			} else {
				nextChar();
			}
		}
	}

	// 忽略单行注释
	private void ignoreInlineComment() throws IOException {
		while (nextChar() != -1 && cursor != '\n')
			;
	}

	// 忽略空白字符和注释
	private void ignoreCommentsAndBlank() throws IOException, JsonException {
		ignoreBlank();
		while (cursor == '/') {
			nextChar();
			if (cursor == '/') {
				ignoreInlineComment();
				nextChar();
			} else if (cursor == '*') {
				ignoreBlockComment();
				nextChar();
			} else {
				throw makeError("Error comment syntax!");
			}
			ignoreBlank();
		}
	}

	// 查找下一个“键-值”对。
	private boolean findNextNamePair() throws IOException, JsonException {
		ignoreCommentsAndBlank();
		if (cursor == '}')
			return false;
		if (cursor != ',')
			throw makeError("Wrong char between key-value pair!");
		nextChar();
		ignoreCommentsAndBlank();
		return true;
	}

	<T> T parseFromJson(Class<T> type, Class<?> componentType)
			throws JsonException {
		try {
			nextChar();
			ignoreCommentsAndBlank();
			return parseFromCurrentLocation(type, componentType);
		} catch (JsonException e) {
			throw e;
		} catch (Exception e) {
			throw makeError(e.getMessage());
		}
	}

	@SuppressWarnings("unchecked")
	private <T> T parseFromCurrentLocation(Class<T> type, Class<?> componentType)
			throws Exception {
		Mirror<T> me = Mirror.me(type);
		switch (cursor) {
		case -1:
			return null;
		case '[': // 如果出现“[”，则表示这是一个数组或List。
			Class<?> compType = componentType;
			boolean returnAsList = true;
			List list = null;

			if (null == type) {
				list = new LinkedList();
			} else if (type.isArray()) {
				list = new LinkedList();
				returnAsList = false;
				compType = type.getComponentType();
			} else if (List.class.isAssignableFrom(type)) {
				returnAsList = true;
				if (me.is(List.class))
					list = new LinkedList();
				else
					list = (List) me.fruit();
			} else {
				throw makeError("'type' can not be '" + type.getName()
						+ "', it should be a Array or List!");
			}
			nextChar();
			ignoreCommentsAndBlank();
			while (cursor != -1 && cursor != ']') {
				Object obj = parseFromCurrentLocation(compType, null);
				list.add(obj);
				ignoreCommentsAndBlank();
				if (cursor == ']')
					break;
				if (cursor != ',')
					throw makeError("Wrong char between elements!");
				nextChar();
				ignoreCommentsAndBlank();
			}
			nextChar();
			if (returnAsList)
				return (T) list;
			Object ary = Array.newInstance(compType, list.size());
			int i = 0;
			for (Iterator iter = list.iterator(); iter.hasNext();) {
				Array.set(ary, i++, Castors.me().castTo(iter.next(), compType));
			}
			return (T) ary;
		case '{': // 如果出现“{”，则表示这是一个对象或Map。
			nextChar();
			ignoreCommentsAndBlank();

			// 如果是Map
			if (Map.class == type)
				me = (Mirror<T>) Mirror.me(HashMap.class);
			if (null == me || Map.class.isAssignableFrom(type)) {
				Map<String, Object> map = (null == me) ? new TreeMap<String, Object>()
						: (Map<String, Object>) me.fruit();
				while (cursor != -1 && cursor != '}') {
					String name = readFieldName();
					Object value = parseFromJson(null, componentType);
					map.put(name, value);
					if (!findNextNamePair())
						break;
				}
				nextChar();
				return (T) map;
			}

			// 如果是对象
			T obj = me.fruit();
			while (cursor != -1 && cursor != '}') {
				Field field = me.getField(readFieldName());
				Class<?> ft = field.getType();
				Class<?> eleType = null;
				if (List.class.isAssignableFrom(ft)) {
					Class<?>[] ts = Mirror.getGenericTypes(field);
					if (ts.length > 0)
						eleType = ts[0];
				} else if (Map.class.isAssignableFrom(ft)) {
					Class<?>[] ts = Mirror.getGenericTypes(field);
					if (ts.length > 1)
						eleType = ts[1];
				}
				Object value = parseFromJson(field.getType(), eleType);
				me.setValue(obj, field, value);
				if (!findNextNamePair())
					break;
			}
			nextChar();
			return obj;
		case 'u': // 没有定义的
			if ('n' != (char) nextChar() & 'd' != (char) nextChar()
					& 'e' != (char) nextChar() & 'f' != (char) nextChar()
					& 'i' != (char) nextChar() & 'n' != (char) nextChar()
					& 'e' != (char) nextChar() & 'd' != (char) nextChar()) {
				throw makeError("String must in qute or it must be null!");
			}
			nextChar();
			return null;
		case 'n': // 为null的情况
			if ('u' != (char) nextChar() & 'l' != (char) nextChar()
					& 'l' != (char) nextChar()) {
				throw makeError("String must in qute or it must be null!");
			}
			nextChar();
			return null;
		case '\'': // String的情况
		case '"':
			StringBuilder sb = readString();
			String value = sb.toString();
			if (null == me || me.is(String.class))
				return (T) value;
			return Castors.me().castTo(value, me.getMirrorType());
		case 't': // true的情况
			if ('u' != (char) nextChar() & 'r' != (char) nextChar()
					& 'e' != (char) nextChar()) {
				throw makeError("Expect boolean as input!");
			}
			if (null != type && !Mirror.me(type).isBoolean()) {
				throw makeError("Expect boolean|Boolean as type!");
			}
			nextChar();
			return (T) Boolean.valueOf(true);
		case 'f': // false的情况
			if ('a' != (char) nextChar() & 'l' != (char) nextChar()
					& 's' != (char) nextChar() & 'e' != (char) nextChar()) {
				throw makeError("Expect boolean as input!");
			}
			if (null != type && !Mirror.me(type).isBoolean()) {
				throw makeError("Expect boolean|Boolean as type!");
			}
			nextChar();
			return (T) Boolean.valueOf(false);
		case '.': // 数字的情况
		case '-':
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
			StringBuilder builder = new StringBuilder();
			do {
				builder.append((char) cursor);
				nextChar();
			} while (cursor != ' ' && cursor != -1 && cursor != ','
					&& cursor != ']' && cursor != '}' && cursor != '/');
			String numVal = Strings.trim(builder);

			// 尝试返回真实的数值（而不是字符串表示的数值）
			if (null != me) {
				if (me.isInt())
					return (T) Integer.valueOf(numVal);
				else if (me.isLong())
					return (T) Long.valueOf(numVal);
				else if (me.isFloat())
					return (T) Float.valueOf(numVal);
				else if (me.isDouble())
					return (T) Double.valueOf(numVal);
				else if (me.isByte())
					return (T) Byte.valueOf(numVal);
			}

			// 猜测一下返回的类型
			if (null == me || me.isNumber() || me.is(Object.class)) {
				char lastChar = Character.toUpperCase(numVal.charAt(numVal
						.length() - 1));
				if (numVal.indexOf('.') >= 0) {
					if (lastChar == 'F')
						return (T) Float.valueOf(numVal.substring(0, numVal
								.length() - 1));
					else
						return (T) Double.valueOf(numVal);
				} else {
					if (lastChar == 'L')
						return (T) Long.valueOf(numVal.substring(0, numVal
								.length() - 1));
					else
						return (T) Integer.valueOf(numVal);
				}
			}
			// 如果还不知道类型，就只能抛出异常了
			throw makeError("type must by one of int|long|float|double|byte");
		case 'v':
			if ('a' == nextChar() && 'r' == nextChar() && ' ' == nextChar()
					&& 'd' == nextChar() && 'e' == nextChar()
					&& 'p' == nextChar() && 'o' == nextChar()
					&& 't' == nextChar() && ' ' == nextChar()
					&& '=' == nextChar() && ' ' == nextChar()
					&& '{' == nextChar()) {
				return parseFromCurrentLocation(type, componentType);
			}
		default:
			throw makeError("Unable to handle this char");
		}
	}

	private String readFieldName() throws IOException {
		if (cursor != '"' && cursor != '\'') {
			StringBuilder sb = new StringBuilder();
			while (cursor != -1 && cursor != ':') {
				sb.append((char) cursor);
				nextChar();
			}
			return Strings.trim(sb);
		}
		String re = readString().toString();
		while (cursor != -1 && cursor != ':')
			nextChar();
		return re;
	}

	private StringBuilder readString() throws IOException {
		StringBuilder sb = new StringBuilder();
		int expEnd = cursor;
		nextChar();
		while (cursor != -1 && cursor != expEnd) {
			if (cursor == '\\') {
				nextChar();
				switch (cursor) {
				case 'n':
					cursor = 10;
					break;
				case 'r':
					cursor = 13;
					break;
				case 't':
					cursor = 9;
					break;
				case 'u':
					char[] hex = new char[4];
					for (int i = 0; i < 4; i++) {
						hex[i] = (char) nextChar();
					}
					cursor = Integer.valueOf(new String(hex), 16);
					break;
				case 'b':
					throw makeError("do not support \\b");
				case 'f':
					throw makeError("do not support \\f");
				}
			}
			sb.append((char) cursor);
			nextChar();
		}
		if (cursor == -1)
			throw makeError("Unclose string");
		nextChar();
		return sb;
	}

	private JsonException makeError(String message) {
		return Exceptions
				.makeThrow(
						JsonException.class,
						"Json syntax error nearby [row:'%d', col:'%d', char:'%c'], reason:'%s'",
						row, col, (char) cursor, message);
	}
}
